//    Openbravo POS is a point of sales application designed for touch screens.
//    Copyright (C) 2007-2009 Openbravo, S.L.
//    http://www.openbravo.com/product/pos
//
//    This file is part of Openbravo POS.
//
//    Openbravo POS is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    Openbravo POS is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with Openbravo POS.  If not, see <http://www.gnu.org/licenses/>.

package com.openbravo.data.loader;

import cn.ekuma.data.dao.I_DeafultFilter;

import com.openbravo.data.loader.query.QBFParameters;
import com.openbravo.data.model.Field;
import com.openbravo.format.Formats;

import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.xml.crypto.Data;

public class TableDefinition {
    
    private I_Session m_s;
    private String tablename;
   
    private String[] fieldname;
    private String[] fieldtran;
    private Datas[] fielddata;
    private Formats[] fieldformat;
    private boolean[] updateIgnoreField;

    private Map<Class,String> parentRefMap;

    private int[] idinx;

    private int dateindex;

    public static int UPDATEDATE_NoSuport=-1;
    public static String LASTMODIFIED="LASTMODIFIED";
    
    I_DeafultFilter filter;
   
        /** Creates a new instance of TableDefinition */
    public TableDefinition(
            I_Session s,
            String tablename, 
            String[] fieldname, String[] fieldtran, Datas[] fielddata, Formats[] fieldformat,
            int[] idinx,int dateinx) {
        m_s = s;
        this.tablename = tablename;       
        
        this.fieldname = fieldname;
        this.fieldtran = fieldtran;
        this.fielddata = fielddata;
        this.fieldformat = fieldformat;
  
        this.idinx = idinx;
        this.dateindex=dateinx;
        parentRefMap=new HashMap<Class,String>();
        updateIgnoreField=null;
    }

    public TableDefinition(
            I_Session s,
            String tablename, 
            String[] fieldname, String[] fieldtran, Datas[] fielddata, Formats[] fieldformat,
            int[] idinx) {  
        this(s, tablename, fieldname, fieldname, fielddata, fieldformat, idinx,UPDATEDATE_NoSuport);
    }

    public TableDefinition(
            I_Session s,
            String tablename, 
            String[] fieldname, Datas[] fielddata, Formats[] fieldformat,
            int[] idinx) {
        this(s, tablename, fieldname, fieldname, fielddata, fieldformat, idinx);
    }    

    public TableDefinition(
            I_Session s,
            String tablename,
            Field[] fields,
            int[] idinx){
        m_s = s;
        this.tablename = tablename;

        this.fieldname = new String[fields.length];
        this.fieldtran = new String[fields.length];
        this.fielddata = new Datas[fields.length];
        this.fieldformat = new Formats[fields.length];
        updateIgnoreField=new boolean[fields.length];
        parentRefMap=new HashMap<Class,String>();
        dateindex=UPDATEDATE_NoSuport;
        for(int i=0;i<fields.length;i++){
            fieldname[i]=fields[i].getLabel();
            fieldtran[i]=fields[i].getLabeltran();
            fielddata[i]=fields[i].getData();
            fieldformat[i]=fields[i].getFormat();
            updateIgnoreField[i]=fields[i].isUpdateIgnore();
            if(fields[i].getParentClass()!=null)
                parentRefMap.put(fields[i].getParentClass(), fields[i].getLabel());
            if(fields[i].getLabel().equalsIgnoreCase(LASTMODIFIED))
            	dateindex=i;
        }
        this.idinx = idinx;
    }

    public int getColumnCount(){
        return fieldname.length;
    }

    public int getUpdateColumnCount(){
    	int realLength=getColumnCount();
    	if(updateIgnoreField==null)
    		return realLength;
    	for(boolean f:updateIgnoreField)
    		if(f)
    			realLength--;
    	return realLength;
    }
    
    public String getTableName() {
        return tablename;
    }

    public I_Session getM_s() {
        return m_s;
    }

    public Formats[] getFieldformat() {
        return fieldformat;
    }

    public String[] getFields() {
        return fieldname;
    }

    public String[] getKeyFields(){
        String[] keyFields=new String[idinx.length];
        for(int i:idinx)
            keyFields[i]=fieldname[idinx[i]];
        return keyFields;
    }


    public Vectorer getVectorerBasic(int[] aiFields) {
        return new VectorerBasic(fieldtran, fieldformat, aiFields);
    }
    
    public IRenderString getRenderStringBasic(int[] aiFields) {
        return new RenderStringBasic(fieldformat, aiFields);
    }
    
    public ComparatorCreator getComparatorCreator(int [] aiOrders) {
        return new ComparatorCreatorBasic(fieldtran, fielddata, aiOrders);
    }
    
    public IKeyGetter getKeyGetterBasic() {
        if (idinx.length == 1) {
            return new KeyGetterFirst(idinx);
        } else {
            return new KeyGetterBasic(idinx);     
        }
    }    
    
    public int getFieldIndex(String field){
    	int ind=-1;
    	for(int i=0;i<this.getColumnCount();i++)
    		if(this.fieldname[i].equalsIgnoreCase(field)){
    			ind=i;
    		   break;
    	     }
    	return ind;
    }
    
    public int[] getFieldsIndex(String[] field){
    	int[] ind=new int[field.length];
    	for(int j=0;j<field.length;j++)
    	  for(int i=0;i<this.getColumnCount();i++)
    		if(this.fieldname[i].equalsIgnoreCase(field[j])){
    			ind[j]=i;
    		     break;
    	     }
    	return ind;
    }
    
    
    public Datas getDatas(int index){
    	return this.fielddata[index];
    }
    
    public void addField(Field aField){
    	int i=fieldname.length;
    	fieldname=Arrays.copyOf(fieldname, i+1);
    	fieldname[i]=aField.getLabel();
    	
    	fieldtran=Arrays.copyOf(fieldtran, i+1);
        fieldtran[i]=aField.getLabeltran();
        
        fielddata=Arrays.copyOf(fielddata, i+1);
        fielddata[i]=aField.getData();
        
        fieldformat=Arrays.copyOf(fieldformat, i+1);
        fieldformat[i]=aField.getFormat();
        this.updateIgnoreField=Arrays.copyOf(updateIgnoreField, i+1);
        updateIgnoreField[i]=aField.isUpdateIgnore();
        
        if(aField.getParentClass()!=null)
            parentRefMap.put(aField.getParentClass(), aField.getLabel());
    }

    public Field[] getTableFields(){
    	Field[] ret=new Field[fieldname.length];
    	for(int i=0;i<fieldname.length;i++){
    		ret[i]=new Field(fieldname[i],fielddata[i],fieldformat[i],fieldtran[i]);
    	}
    	return ret;
    }
    
    
    public SerializerRead getSerializerReadBasic() {
        return new SerializerReadBasic(fielddata);
    }

    public SerializerRead getSerializerReadBasic(int[] fieldindx) {
        return new SerializerReadBasicExt(fielddata,fieldindx);
    }

    public SerializerWrite getSerializerInsertBasic(int[] fieldindx) {
        return new SerializerWriteBasicExt(fielddata, fieldindx);
    }
    
    public SerializerWrite getSerializerDeleteBasic() {     
        return new SerializerWriteBasicExt(fielddata, idinx);
    }
    
    public SerializerWrite getSerializerExistBasic() {     
        return new SerializerWriteBasicExt(fielddata, idinx);
    }
    
    public SerializerWrite getSerializerUpdateBasic(int[] fieldindx) {
        
        int[] aindex = new int[fieldindx.length + idinx.length];

        System.arraycopy(fieldindx, 0, aindex, 0, fieldindx.length);
        
        System.arraycopy(idinx, 0, aindex, fieldindx.length, idinx.length);
 
        return new SerializerWriteBasicExt(fielddata, aindex);
    }
    
    public SentenceList getListSentence() {
        return getListSentence(getSerializerReadBasic());
    }
    
    public SentenceList getListSentence(SerializerRead sr) {
        return new PreparedSentence(m_s, getListSQL()+getDefaultListOrder(null), null,  sr);
    }
    
    public String getListSQL() {
        return getListSQL(getAllFields());
    }

    public String getListSQL(int[] fieldindx) {

        StringBuilder sent = new StringBuilder();
        sent.append("select ");

        for (int i = 0; i < fieldindx.length; i ++) {
            if (i > 0) {
                sent.append(", ");
            }
            sent.append(fieldname[fieldindx[i]]);
        }

        sent.append(" from ");
        sent.append(tablename);

        return sent.toString();
    }

    public String getListSQL(String alise){
        return getListSQL(alise,getAllFields());
    }

    public String getListSQL(String alise,int[] fieldindx) {

        StringBuilder sent = new StringBuilder();
        sent.append("select ");

        for (int i = 0; i < fieldindx.length; i ++) {
            if (i > 0) {
                sent.append(", ");
            }
            sent.append(alise+"."+fieldname[fieldindx[i]]);
        }

        sent.append(" from ");
        sent.append(tablename+" "+alise+" ");
        
        return sent.toString();
    }


	public SentenceFind getFindSentence(){
        return getFindSentence(null);
    }

    public SentenceFind getFindSentence(SerializerRead sr){
        return getFindSentence(sr,null);
    }

    public SentenceFind getFindSentence(SerializerRead sr,SerializerWrite sw){
        return new PreparedSentence(m_s, getFindSQL(), sw,  sr);
    }

    public String getFindSQL(){
        return getFindSQL(getAllFields());
    }

     public String getFindSQL(int[] fieldindx) {
        StringBuilder sent = new StringBuilder();
        sent.append("select ");

        for (int i = 0; i < fieldindx.length; i ++) {
            if (i > 0) {
                sent.append(", ");
            }
            sent.append(fieldname[fieldindx[i]]);
        }

        sent.append(" from ");
        sent.append(tablename);

         for (int i = 0; i < idinx.length; i ++) {
            sent.append((i == 0) ? " where " : " and ");
            sent.append(fieldname[idinx[i]]);
            sent.append(" = ?");
        }

        return sent.toString();
     }

    public SentenceExec getDeleteSentence() {
        return getDeleteSentence(getSerializerDeleteBasic());
    }
    
    public SentenceExec getDeleteSentence(SerializerWrite sw) {
        return new PreparedSentence(m_s, getDeleteSQL(), sw, null);
    }
    
    public String getDeleteSQL() {
        
        StringBuilder sent = new StringBuilder();
        sent.append("delete from ");
        sent.append(tablename);
        
        for (int i = 0; i < idinx.length; i ++) {
            sent.append((i == 0) ? " where " : " and ");
            sent.append(fieldname[idinx[i]]);
            sent.append(" = ?");
        }
        
        return sent.toString();     
    }
   
    public String getDeleteSQL(String[] fields) {
        
        StringBuilder sent = new StringBuilder();
        sent.append("delete from ");
        sent.append(tablename);
        
        for (int i = 0; i < fields.length; i ++) {
            sent.append((i == 0) ? " where " : " and ");
            sent.append(fields[i]);
            sent.append(" = ?");
        }
        
        return sent.toString();     
    }
    
    
    public SentenceExec getClearSentence() {
        return new StaticSentence(m_s, getClearSQL(), null, null);
    }
    
    public String getClearSQL(){
    	return "delete from "+tablename;
    }
    
    
    public SentenceExec getInsertSentence() {
        return getInsertSentence(getAllFields());
    }
    
    public SentenceExec getInsertSentence(int[] fieldindx) {
        return new PreparedSentence(m_s, getInsertSQL(fieldindx), getSerializerInsertBasic(fieldindx), null);
    }

    public SentenceExec getInsertSentence(SerializerWrite objWrite) {
         return new PreparedSentence(m_s, getInsertSQL(getAllFields()), objWrite, null);
    }

    public String getInsertSQL(int[] fieldindx) {
        
        StringBuilder sent = new StringBuilder();
        StringBuilder values = new StringBuilder();
        
        sent.append("insert into ");
        sent.append(tablename);
        sent.append(" (");        
        
        for (int i = 0; i < fieldindx.length; i ++) {
            if (i > 0) {
                sent.append(", ");
                values.append(", ");
            }
            sent.append(fieldname[fieldindx[i]]);
            values.append("?");
        }
        
        sent.append(") values (");
        sent.append(values.toString());
        sent.append(")");

        return sent.toString();       
    }
    
    public int[] getAllFields() {
        
        int[] fieldindx = new int[fieldname.length];
        for (int i = 0; i < fieldname.length; i++) {
            fieldindx[i] = i;
        }
        return fieldindx;        
    }

    
    public SentenceFind getFindByFieldIndexSentence(int index) {
		return new StaticSentence(m_s,getFindSQL(new int[]{index}) , getSerializerExistBasic(), new SerializerReadDatas(fielddata[index]));
	}

    public  SentenceExec getCountSentence(){
        return new StaticSentence(m_s,getCountSql() , null, SerializerReadInteger.INSTANCE);
    }

    public  SentenceExec getCountSentence(QBFParameters filter){
        return new StaticSentence(m_s,getCountSql() , null, SerializerReadInteger.INSTANCE);
    }
    
    public String getCountSql(){
        return "SELECT COUNT(*) FROM "+tablename;
    }


    public SentenceFind getExistSentence(SerializerWrite rowWriter){
        return new StaticSentence(m_s,getExistSql() , rowWriter, SerializerReadInteger.INSTANCE);
    }
    
    public String getExistSql(){
        StringBuilder sent = new StringBuilder();
        sent.append(getCountSql());
        for (int i = 0; i < idinx.length; i ++) {
            sent.append((i == 0) ? " where " : " and ");
            sent.append(fieldname[idinx[i]]);
            sent.append(" = ?");
        }
       return sent.toString();
    }
    
    public SentenceFind getExistSentence(String[] fields){
        return new StaticSentence(m_s,getExistSql(fields) , getSerializerWriteBase(fields), SerializerReadInteger.INSTANCE);
    }
    
    
    public String getExistSql(String[] fields){
        StringBuilder sent = new StringBuilder();
        sent.append(getCountSql());
        for (int i = 0; i < fields.length; i ++) {
            sent.append((i == 0) ? " where " : " and ");
            sent.append(fields[i]);
            sent.append(" = ?");
        }
       return sent.toString();
    }

    public SentenceExec getUpdateSentence() {
        return getUpdateSentence(getAllFields());
    }
    
    public SentenceExec getUpdateSentence(int[] fieldindx) {
        return new PreparedSentence(m_s, getUpdateSQL(fieldindx), getSerializerUpdateBasic(fieldindx), null);
    }

    public SentenceExec getUpdateSentence(int[] fieldindx,SerializerWrite rowWriter) {
        return new PreparedSentence(m_s, getUpdateSQL(fieldindx), rowWriter, null);
    }
    
    public SentenceExec getUpdateSentence(SerializerWrite rowWriter) {
        return new PreparedSentence(m_s, getUpdateSQL(getAllFields()), rowWriter, null);
    }
    
    public String getUpdateSQL(int[] fieldindx) { 
        StringBuilder sent = new StringBuilder();
        sent.append("update ");
        sent.append(tablename);
        sent.append(" set ");
        int j=0;
        for (int i = 0; i < fieldindx.length; i ++) {
        	if(updateIgnoreField==null||!updateIgnoreField[i]){//注在使用普通模式时应设过滤条件为null;
        	  if (j > 0) {
                  sent.append(", ");
              }  
              sent.append(fieldname[fieldindx[i]]);
              sent.append(" = ?");
              j++;
        	}
        }
        for (int i = 0; i < idinx.length; i ++) {
            sent.append((i == 0) ? " where " : " and ");
            sent.append(fieldname[idinx[i]]);
            sent.append(" = ?");
        }
        return sent.toString();               
    }

    public String getUpdateWhoutIgnoreSQL(int[] fieldindx) { 
        StringBuilder sent = new StringBuilder();
        sent.append("update ");
        sent.append(tablename);
        sent.append(" set ");
        int j=0;
        for (int i = 0; i < fieldindx.length; i ++) {
        	  if (j > 0) {
                  sent.append(", ");
              }  
              sent.append(fieldname[fieldindx[i]]);
              sent.append(" = ?");
              j++;
        }
        for (int i = 0; i < idinx.length; i ++) {
            sent.append((i == 0) ? " where " : " and ");
            sent.append(fieldname[idinx[i]]);
            sent.append(" = ?");
        }
        return sent.toString();               
    }
    
    
    public String getDateFieldName(){
        if(dateindex!=UPDATEDATE_NoSuport)
            return fieldname[dateindex];
        return null;
    }

    public TableDefinition getInfoTableDefinition(int[] indexs){
       String[] fieldname1=new String[indexs.length];
       String[] fieldtran1=new String[indexs.length];
       Datas[] fielddata1=new Datas[indexs.length];
       Formats[] fieldformat1=new Formats[indexs.length];
       int[] idinx1=new int[idinx.length];
       for(int i=0;i<indexs.length;i++){
           fieldname1[i]=fieldname[indexs[i]];
           fieldtran1[i]=fieldtran[indexs[i]];
           fielddata1[i]=fielddata[indexs[i]];
           fieldformat1[i]=fieldformat[indexs[i]];
           for(int j=0;j<idinx.length;j++){
               if(idinx[j]==indexs[i])
                   idinx1[j]=i;
           }
       }
      return new TableDefinition(m_s,tablename, fieldname1, fieldtran1, fielddata1,fieldformat1,idinx1);
    }

    public String getSelectQBFSQL(){
    	return getSelectQBFSQL(getAllFields());
    }
    
    public String getSelectQBFSQL(int[] fieldindx) {
        StringBuilder sent = new StringBuilder();
        sent.append("select ");

        for (int i = 0; i < fieldindx.length; i ++) {
            if (i > 0) {
                sent.append(", ");
            }
            sent.append(fieldname[fieldindx[i]]);
        }
        sent.append(" from ");
        sent.append(tablename);
        sent.append(" WHERE ?(QBF_FILTER) ");
        sent.append(getDefaultListOrder(null));
        
        return sent.toString();
    }

    public String getParentFieldName(Class c){
        return parentRefMap.get(c);
    }

    public String getDeleteQBFSQL(){
    	 StringBuilder sent = new StringBuilder();
         sent.append("delete from ");
         sent.append(tablename);
         sent.append(" WHERE ?(QBF_FILTER) ");

         return sent.toString();
    }
    
    
    public String getCountQBFSQL(){
    	return getCountSql()+" WHERE ?(QBF_FILTER) ";
    }
    
    public String getDefaultFilter(String pref){
		if(filter!=null)
			return filter.getDefaultFilter(pref);
		return "(1=1)";
	}
    
	public I_DeafultFilter getFilter() {
		return filter;
	}

	public void setFilter(I_DeafultFilter filter) {
		this.filter = filter;
	}

	public Datas[] getDatas() {
		return fielddata;
	} 
	
	public String getDefaultListOrder(String alise){
		return "";
	}

	public int[] getIdinx() {
		return idinx;
	}
	
	public String getListSQL(Date start,Date end){
        String sql=getListSQL();
        String dateField=getDateFieldName();   
        if(dateField!=null){
     	   sql+=" where ";
     	   if(start!=null&&end!=null){
     		   sql+=getDateFieldName()+">="+DataWriteUtils.getSQLValue(start);
                sql+=" and "+getDateFieldName()+"<="+DataWriteUtils.getSQLValue(end);
     	   }else if(start!=null){
     		   sql+=getDateFieldName()+">="+DataWriteUtils.getSQLValue(start);
     	   }else if(end!=null){
     		   sql+=getDateFieldName()+"<="+DataWriteUtils.getSQLValue(end);
     	   }else
     		   sql+="(1=1)";
        }
        return sql;
 }

	public Datas getSerializerWriteData(String field) {
		return fielddata[Arrays.asList(fieldname).indexOf(field)];
	}
	
	
	private SerializerWrite getSerializerWriteBase(String[] fields){
		if(fields.length==1)
			return new SerializerWriteBasic(getSerializerWriteData(fields[0]));
		Datas[] datas1=new Datas[fields.length];
		for(int i=0;i<fields.length;i++)
			datas1[i]=getSerializerWriteData(fields[i]);
		return new SerializerWriteBasic(datas1);
	}

	
}
